﻿#region Using Directives

using System;
using System.Xml;

#endregion Using Directives

namespace SharePointStu.CAMLHelper.CAML
{
    /// <summary>
    /// Class that defines a single field query element of a CAML query.
    /// </summary>
    internal sealed class Query : BaseElement
    {
        #region Private Members

        /// <summary>
        /// {0} = Type of query
        /// {1} = Types.FieldRefType
        /// {2} = Name or ID of field
        /// {3} = Type of field
        /// {4} = Value of field
        /// </summary>
        const string CAML = "<{0}><FieldRef {1}='{2}'/><Value Type='{3}'>{4}</Value></{0}>";

        /// <summary>
        /// {0} = Type of query
        /// {1} = Types.FieldRefType
        /// {2} = Name or ID of field
        /// </summary>
        const string CAML_NOVALUE = "<{0}><FieldRef {1}='{2}'/></{0}>";

        string _value;
        Types.FieldTypes _fieldType;
        Types.QueryTypes _queryType;

        #endregion Private Members

        #region Public Properties

        /// <summary>
        /// Get or set the value of the SharePoint field that is being queried.
        /// </summary>
        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }

        /// <summary>
        /// Get or set the type of the field being queried.
        /// </summary>
        public Types.FieldTypes FieldType
        {
            get { return _fieldType; }
            set { _fieldType = value; }
        }

        /// <summary>
        /// Get or set the type of query to perform.
        /// </summary>
        public Types.QueryTypes QueryType
        {
            get { return _queryType; }
            set { _queryType = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Initialise the <see cref="T:SharePointStu.CAMLHelper.CAML.Query"/> object.
        /// </summary>
        /// <param name="field">The value of the field identifier to query against.</param>
        /// <param name="fieldRefType">The <see cref="T:SharePointStu.CAMLHelper.CAML.Types.FieldRefTypes"/> object type of the field being queried.</param>
        /// <param name="fieldType">The <see cref="T:SharePointStu.CAMLHelper.CAML.Types.FieldTypes"/> object of the field.</param>
        /// <param name="queryType">The <see cref="T:SharePointStu.CAMLHelper.CAML.Types.FieldRefTypes"/> object that defines the type of query to perform.</param>
        /// <param name="value">The value of the field being queried.</param>
        public Query(string field, Types.FieldRefTypes fieldRefType, Types.FieldTypes fieldType,
                         Types.QueryTypes queryType, string value)
            : base(field, fieldRefType)
        {
            FieldType = fieldType;
            QueryType = queryType;
            Value = value;

            Validate();
        }

        #endregion Constructors

        #region Public Methods

        /// <summary>
        /// Get the CAML code for the individual query.
        /// </summary>
        /// <returns>CAML string for the individual query.</returns>
        public override string GetCAML()
        {
            string caml = string.Empty;

            if (!string.IsNullOrEmpty(this.Field))
            {
                if (this.QueryType == Types.QueryTypes.IsNull || this.QueryType == Types.QueryTypes.IsNotNull)
                {
                    caml = string.Format(CAML_NOVALUE, this.QueryType.ToString(), this.FieldRefType.ToString(), this.Field);
                }
                else
                {
                    if (Validate())
                    {
                        caml = string.Format(CAML, this.QueryType.ToString(), this.FieldRefType.ToString(), this.Field, this.FieldType.ToString(), this.Value);
                    }
                }
            }

            return caml;
        }

        /// <summary>
        /// Get the CAML code for the individual query.
        /// </summary>
        /// <returns>CAML for the individual query as an XmlDocument.</returns>
        public XmlDocument GetCAMLAsXML()
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(GetCAML());

            return doc;
        }

        #endregion Public Methods

        #region Protected Methods

        /// <summary>
        /// Validate the values of the <see cref="T:SharePointStu.CAMLHelper.CAML.Query"/> object.
        /// </summary>
        /// <returns>bool: if the <see cref="T:SharePointStu.CAMLHelper.CAML.Query"/> object values are valid.</returns>
        protected override bool Validate()
        {
            base.Validate();

            // Check the value for the query matches the data type of the field.
            switch (this.FieldType)
            {
                case Types.FieldTypes.YesNo:
                    bool boolValue;
                    if (!bool.TryParse(this.Value, out boolValue))
                        throw new InvalidFieldValueException(this.Field, this.FieldType, this.Value);
                    break;
                case Types.FieldTypes.Counter:
                    int counterValue;
                    if (!int.TryParse(this.Value, out counterValue))
                        throw new InvalidFieldValueException(this.Field, this.FieldType, this.Value);
                    break;
                case Types.FieldTypes.Number:
                    float numberValue;
                    if (!float.TryParse(this.Value, out numberValue))
                        throw new InvalidFieldValueException(this.Field, this.FieldType, this.Value);
                    break;
                case Types.FieldTypes.DateTime:
                    DateTime dateValue;
                    if (!DateTime.TryParse(this.Value, out dateValue))
                        throw new InvalidFieldValueException(this.Field, this.FieldType, this.Value);
                    break;
            }

            return true;
        }

        #endregion Protected Methods
    }
}
